package com.jfirer.validator.validation;

import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.util.Map;
import java.util.Set;
import javax.validation.BootstrapConfiguration;
import javax.validation.Configuration;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
import javax.validation.ParameterNameProvider;
import javax.validation.Path;
import javax.validation.Path.Node;
import javax.validation.TraversableResolver;
import javax.validation.ValidationException;
import javax.validation.ValidationProviderResolver;
import javax.validation.ValidatorFactory;
import javax.validation.spi.ConfigurationState;
import javax.validation.spi.ValidationProvider;
import com.jfirer.validator.PropertyValueDescriptorFactory;
import com.jfirer.validator.util.DefaultParameterNameProvider;
import com.jfirer.validator.util.ReflectPropertyValueDescriptorFactory;

public class JfireValidatorConfiguration implements Configuration<JfireValidatorConfiguration>
{
    private MessageInterpolator                             customInterpolator;
    private MessageInterpolator                             defaultInterpolator               = new DefaultMessageInterpolator();
    private ConstraintValidatorFactory                      customConstraintValidatorFactory;
    private ConstraintValidatorFactory                      defaultConstraintValidatorFactory = new DefaultConstraintValidatorFactory();
    private ValidationProviderResolver                      validationProviderResolver;
    private ValidationProvider<JfireValidatorConfiguration> specializedprovider;
    private ParameterNameProvider                           customParameterNameProvider;
    private ParameterNameProvider                           defaultParameterNameProvider      = new DefaultParameterNameProvider();
    private PropertyValueDescriptorFactory                  propertyValueDescriptorFactory    = new ReflectPropertyValueDescriptorFactory();
    
    public JfireValidatorConfiguration(ValidationProviderResolver validationProviderResolver)
    {
        this.validationProviderResolver = validationProviderResolver;
    }
    
    public JfireValidatorConfiguration(ValidationProvider<JfireValidatorConfiguration> specializedprovider)
    {
        this.specializedprovider = specializedprovider;
    }
    
    @Override
    public JfireValidatorConfiguration ignoreXmlConfiguration()
    {
        return this;
    }
    
    @Override
    public JfireValidatorConfiguration messageInterpolator(MessageInterpolator interpolator)
    {
        customInterpolator = interpolator;
        return this;
    }
    
    @Override
    public JfireValidatorConfiguration traversableResolver(TraversableResolver resolver)
    {
        return this;
    }
    
    @Override
    public JfireValidatorConfiguration constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory)
    {
        customConstraintValidatorFactory = constraintValidatorFactory;
        return this;
    }
    
    public JfireValidatorConfiguration propertyValueDescriptorFactory(PropertyValueDescriptorFactory propertyValueDescriptorFactory)
    {
        this.propertyValueDescriptorFactory = propertyValueDescriptorFactory;
        return this;
    }
    
    @Override
    public JfireValidatorConfiguration addMapping(InputStream stream)
    {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public JfireValidatorConfiguration addProperty(String name, String value)
    {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public MessageInterpolator getDefaultMessageInterpolator()
    {
        return defaultInterpolator;
    }
    
    @Override
    public TraversableResolver getDefaultTraversableResolver()
    {
        return new TraversableResolver() {
            
            @Override
            public boolean isReachable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType)
            {
                return true;
            }
            
            @Override
            public boolean isCascadable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType)
            {
                return true;
            }
        };
    }
    
    @Override
    public ConstraintValidatorFactory getDefaultConstraintValidatorFactory()
    {
        return defaultConstraintValidatorFactory;
    }
    
    @Override
    public ValidatorFactory buildValidatorFactory()
    {
        if (specializedprovider != null)
        {
            return specializedprovider.buildValidatorFactory(new JfireValidatorConfigurationState());
        }
        else
        {
            return validationProviderResolver.getValidationProviders().get(0).buildValidatorFactory(new JfireValidatorConfigurationState());
        }
    }
    
    public class JfireValidatorConfigurationState implements ConfigurationState
    {
        public PropertyValueDescriptorFactory getPropertyValueDescriptorFactory()
        {
            if (propertyValueDescriptorFactory == null)
            {
                return new ReflectPropertyValueDescriptorFactory();
            }
            return propertyValueDescriptorFactory;
        }
        
        @Override
        public boolean isIgnoreXmlConfiguration()
        {
            return true;
        }
        
        @Override
        public MessageInterpolator getMessageInterpolator()
        {
            if (customInterpolator != null)
            {
                return customInterpolator;
            }
            return getDefaultMessageInterpolator();
        }
        
        @Override
        public Set<InputStream> getMappingStreams()
        {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public ConstraintValidatorFactory getConstraintValidatorFactory()
        {
            if (customConstraintValidatorFactory != null)
            {
                return customConstraintValidatorFactory;
            }
            return getDefaultConstraintValidatorFactory();
        }
        
        @Override
        public TraversableResolver getTraversableResolver()
        {
            return new TraversableResolver() {
                
                @Override
                public boolean isReachable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType)
                {
                    return true;
                }
                
                @Override
                public boolean isCascadable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType)
                {
                    return true;
                }
            };
        }
        
        @Override
        public Map<String, String> getProperties()
        {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public ParameterNameProvider getParameterNameProvider()
        {
            if (customParameterNameProvider != null)
            {
                return customParameterNameProvider;
            }
            return getDefaultParameterNameProvider();
        }
        
    }
    
    @Override
    public JfireValidatorConfiguration parameterNameProvider(ParameterNameProvider parameterNameProvider)
    {
        return this;
    }
    
    @Override
    public ParameterNameProvider getDefaultParameterNameProvider()
    {
        return defaultParameterNameProvider;
    }
    
    @Override
    public BootstrapConfiguration getBootstrapConfiguration()
    {
        throw new ValidationException(new UnsupportedOperationException());
    }
}
